#ifndef VL_SHARED_PTR
#define VL_SHARED_PTR

#include <cassert>
#include <algorithm>
#include "list.hpp"

namespace vl
{

struct weak_ptr_common;

struct shared
{
	shared()
	: _ref_count(0), _first(0)
	{

	}
	
	void add_ref()
	{
		++_ref_count;
	}
	
	void release();
	
	virtual ~shared()
	{
	}
		
	int _ref_count;
	weak_ptr_common* _first;
};

struct shared_ptr_common
{
	shared_ptr_common()
	: v(0)
	{
	}
	
	shared_ptr_common(shared* v_init)
	{
		_set(v_init);
	}
	
	shared_ptr_common(shared_ptr_common const& b)
	{
		_set(b.v);
	}
		
	~shared_ptr_common()
	{
		_release();
	}
		
	void reset(shared* v)
	{
		_release();
		_set(v);
	}
	
	void _release()
	{
		if(v)
			v->release();
	}
	
	void _set(shared* v_new)
	{
		v = v_new;
		if(v)
			v->add_ref();
	}
	
	shared* v;
};

template<typename T>
struct deferred_ptr;

template<typename T>
struct shared_ptr : shared_ptr_common
{
	shared_ptr()
	{
	}
	
	shared_ptr(T* v)
	: shared_ptr_common(v)
	{
	}
	
	shared_ptr(shared_ptr const& b)
	: shared_ptr_common(b)
	{
	}
	
	template<typename SrcT>
	shared_ptr(shared_ptr<SrcT> const& b)
	{
		T* p = b.ptr();
		shared_ptr_common::_set(p);
	}
	
	shared_ptr(deferred_ptr<T> const& b);
	
	shared_ptr& operator=(deferred_ptr<T> const& b);
	
	shared_ptr& operator=(shared_ptr const& b)
	{
		shared_ptr_common::reset(b.v);
		return *this;
	}
	
	template<typename SrcT>
	shared_ptr& operator=(shared_ptr<SrcT> const& b)
	{
		T* p = b.ptr();
		shared_ptr_common::reset(p);
		return *this;
	}
	
	shared_ptr& operator=(T* v_new)
	{
		shared_ptr_common::reset(v_new);
		return *this;
	}
	
	operator void*() const
	{
		return v;
	}
	
	T* operator->() const
	{
		assert(v);
		return static_cast<T*>(v);
	}
	
	T& operator*() const
	{
		assert(v);
		return *static_cast<T*>(v);
	}
	
	void reset(T* b)
	{
		shared_ptr_common::reset(b);
	}
	
	void reset()
	{
		_release();
		v = 0;
	}
	
	void swap(shared_ptr& b)
	{
		std::swap(v, b.v);
	}
	
	template<typename DestT>
	shared_ptr<DestT> cast()
	{
		return dynamic_cast<DestT*>(ptr());
	}
	
	T* ptr() const
	{
		return static_cast<T*>(v);
	}
};

template<typename T>
struct deferred_ptr
{
	deferred_ptr()
	: v(0)
	{
	}
	
	deferred_ptr(T* v)
	: v(v)
	{
	}
	
	deferred_ptr(shared_ptr<T> const& b)
	: v(static_cast<T*>(b.v))
	{
	}
		
	operator void*() const
	{
		return v;
	}
	
	T* operator->() const
	{
		assert(v);
		return v;
	}
	
	T& operator*() const
	{
		assert(v);
		return *v;
	}
		
	void swap(deferred_ptr& b)
	{
		std::swap(v, b.v);
	}
	
	T* v;
};

template<typename T>
shared_ptr<T>::shared_ptr(deferred_ptr<T> const& b)
: shared_ptr_common(b.v)
{

}

template<typename T>
shared_ptr<T>& shared_ptr<T>::operator=(deferred_ptr<T> const& b)
{
	shared_ptr_common::reset(b.v);
	
	return *this;
}

struct shared_releaser
{
	void operator()(shared* p)
	{
		p->release();
	}
};

struct shared_ownership
{
	shared* take(shared* p)
	{
		p->add_ref();
		return p;
	}
};

template<typename T, typename Tag = default_list_tag>
struct shared_list : list<T, shared_releaser, Tag, shared_ownership>
{
	shared_list()
	{
	}
};

}

#endif //VL_SHARED_PTR
